package secrets

import (
	"fmt"
	"os"
	"path/filepath"

	"apiote.xyz/p/eeze/crypto"
	"apiote.xyz/p/eeze/fs"

	"git.sr.ht/~sircmpwn/go-bare"
)

type Index struct {
	LabelIndex map[string]map[string][]string
	URLIndex   map[string]map[string][]string
}

func RecreateIndex(key Key) error {
	index := Index{}
	err := filepath.Walk(fs.DataHome()+"/secrets/", func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return err
		}
		if err != nil {
			return err
		}
		id := SecretEntry{Value: info.Name()}
		secrets, err := Get([]string{id.Value}, key)
		if err != nil {
			return fmt.Errorf("while getting secret %s: %s", id.Value, err)
		}
		index.add(secrets[0], id)
		return nil
	})
	if err != nil {
		return fmt.Errorf("while walking through secrets: %s", err)
	}
	err = index.write(key.bytes)
	if err != nil {
		return fmt.Errorf("while writing index: %s", err)
	}
	return nil
}

func openIndex(key []byte) (Index, error) {
	dataHome := fs.DataHome()
	index := Index{}
	indexBytes, err := os.ReadFile(dataHome + "/index.bare")
	if err != nil {
		if e, ok := err.(*os.PathError); ok && e.Err.Error() == "no such file or directory" {
			err = nil
		} else {
			return index, fmt.Errorf("while reading index: %w", err)
		}
	} else {
		indexBytes, err = crypto.Decrypt(indexBytes, key)
		if err != nil {
			return index, fmt.Errorf("while decrypting index: %w", err)
		}
		err = bare.Unmarshal(indexBytes, &index)
		if err != nil {
			return index, fmt.Errorf("while unmarshalling index: %w", err)
		}
	}
	return index, nil
}

func (index *Index) remove(secret Secret, id SecretEntry) {
	if index.LabelIndex == nil {
		return
	}
	if index.LabelIndex[secret["label"].Value] == nil {
		return
	}
	newByLabel := []string{}
	byLabel := index.LabelIndex[secret["label"].Value][secret["username"].Value]
	for _, value := range byLabel {
		if value != id.Value {
			newByLabel = append(newByLabel, value)
		}
	}
	index.LabelIndex[secret["label"].Value][secret["username"].Value] = newByLabel

	if index.URLIndex == nil {
		return
	}
	if index.URLIndex[secret["url"].Value] == nil {
		return
	}
	newByURL := []string{}
	byURL := index.URLIndex[secret["url"].Value][secret["username"].Value]
	for _, value := range byURL {
		if value != id.Value {
			newByURL = append(newByURL, value)
		}
	}
	index.URLIndex[secret["url"].Value][secret["username"].Value] = newByURL
}

func (index *Index) add(secret Secret, id SecretEntry) {
	if index.LabelIndex == nil {
		index.LabelIndex = map[string]map[string][]string{}
	}
	if index.LabelIndex[secret["label"].Value] == nil {
		index.LabelIndex[secret["label"].Value] = map[string][]string{}
	}
	byLabel := index.LabelIndex[secret["label"].Value][secret["username"].Value]
	byLabel = append(byLabel, id.Value)
	index.LabelIndex[secret["label"].Value][secret["username"].Value] = byLabel

	if index.URLIndex == nil {
		index.URLIndex = map[string]map[string][]string{}
	}
	if index.URLIndex[secret["url"].Value] == nil {
		index.URLIndex[secret["url"].Value] = map[string][]string{}
	}
	byURL := index.URLIndex[secret["url"].Value][secret["username"].Value]
	byURL = append(byURL, id.Value)
	index.URLIndex[secret["url"].Value][secret["username"].Value] = byURL
}

func (index Index) write(key []byte) error {
	dataHome := fs.DataHome()
	indexBytes, err := bare.Marshal(&index)
	if err != nil {
		return fmt.Errorf("while marshalling index: %s", err)
	}
	indexBytes, err = crypto.Encrypt(indexBytes, key)
	if err != nil {
		return fmt.Errorf("while encrypting index: %s", err)
	}
	os.WriteFile(dataHome+"/index.bare", indexBytes, 0644)
	return nil
}
